package com.runa.monitor.platinterface.extend.hdb;


import com.alibaba.fastjson.JSON;
import com.hdb.api.adv.HdbClctrData;
import com.hdb.api.adv.HdbData;
import com.hdb.api.adv.HistoryCriteria;
import com.hdb.api.adv.WriteStatus;
import com.hdb.api.base.FLTHClctrResult;
import com.hdb.api.base.FLTHWriteResult;
import com.hdb.api.client.HdbClient;
import com.runa.monitor.platinterface.backwork.util.FileUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Component;

import java.io.BufferedReader;
import java.net.URL;
import java.util.*;

@Slf4j
@Component
public class GatewayHdbClient /*implements CommandLineRunner*/ {
    private static GatewayHdbClient instance;

    private final HdbClient hdbClient = new HdbClient();


    /**
     * GatewayHdbClientSuccess----[{"alarmType":"NONE","quality":"GOOD","timestamp":1699513234059,"type":"R4","value":-87.12000274658203},{"alarmType":"NONE","quality":"GOOD","timestamp":1699513234059,"type":"R4","value":37.880001068115234},{"alarmType":"NONE","quality":"GOOD","timestamp":1699513234059,"type":"R4","value":0.0}]
     *
     * @param args
     * @throws Exception
     */
//    @Override
    public void run(String... args) throws Exception {
//        String hdbUrl = "/runa/hdb.txt";
////        log.info(resource.getPath());
////        URL resource = getClass().getClassLoader().getResource("hdb.txt");
////        URL resource = Thread.currentThread().getContextClassLoader().getResource("hdb.txt");
////        String projectPath = null;
////        if (resource != null) {
////            projectPath = resource.getPath();
////        }else {
////            log.error("目录下无此文件hdb.txt！！！！！！");
////            return;
////        }
////            System.out.println("当前项目路径：" + projectPath);
//        BufferedReader bufferedReader = FileUtils.getBufferedReader(hdbUrl);//
//        String line;
//        while ((line = bufferedReader.readLine()) != null) {
////            String[] strings = line.split("\\s+");//通过空格切割
////            String[] strings = line.split("\\(");//通过(切割
//
////            String[] stringArr = line.split(", \"");
////
////            String replace = stringArr[2].replace("\"", "");
////            String substring = replace.substring(0, replace.length() - 2);
//            log.info(line);
//        }
//        bufferedReader.close();


        log.info("GatewayHdbClient开始测试HDB连接。。。。。。");
        String[] tags = new String[8];
//        tags[0] = "M_Jlxy_1_1Unit.L_GoTemp";

//        tags[0] = "HDB.DEMO计算采集器.R4001";
//        tags[1] = "DEMO计算采集器.R4002";
//        tags[2] = "DEMO计算采集器.B0001";

//        tags[0] = "M_Jlxy_1_1Unit.L_GoTemp";
//        tags[1] = "M_Jlxy_1_1Unit.L_BackTemp";
//        tags[2] = "M_Jlxy_1_1Unit.L_Flow";


        tags[0] = "M_Llsj_2_1Unit.L_GoTemp";
        tags[1] = "M_Jlxy_1_1Unit.L_BackTemp";
        tags[2] = "M_Jlxy_1_1Unit.L_Flow";
        tags[3] = "M_Jlxy_1_1Unit.L_Hot";
        tags[4] = "M_Jlxy_1_1Unit.L_TotalFlow";
        tags[5] = "M_Jlxy_1_1Unit.L_TotalHot";
        tags[6] = "M_Jlxy_1_1Unit.L_GoPress";
        tags[7] = "M_Jlxy_1_1Unit.L_BackPress";


        HdbClient hdbClient = new HdbClient();
        hdbClient.setLoginInfo(HdbProperty.getUser(), "");
//        hdbClient.setConfig("10.2.1.53", 9061, 10000);
        hdbClient.setConfig(HdbProperty.getIp(), HdbProperty.getPort(), HdbProperty.getTimeout());
        HdbData[] hdbData = hdbClient.ReadCurrentByName(tags, false, false, false);
        if (null == hdbData) {
            String errorString = hdbClient.GetErrorString(hdbClient.getLastError());
            log.info("GatewayHdbClient----" + errorString);
            System.out.println(errorString);
        } else {
//            log.info("GatewayHdbClientSuccess----"+ Arrays.toString(hdbData));
            log.info("GatewayHdbClientSuccess----" + JSON.toJSONString(hdbData));
            System.out.println(JSON.toJSONString(hdbData));
        }
    }

    /**
     * M_Jlxy_1_1Unit	L_GoTemp
     *
     * @param args
     */
    public static void main(String[] args) {
        String[] tags = new String[3];
//        tags[0] = "M_Jlxy_1_1Unit.L_GoTemp";

//        tags[0] = "HDB.DEMO计算采集器.R4001";
//        tags[1] = "DEMO计算采集器.R4002";
//        tags[2] = "DEMO计算采集器.B0001";

        tags[0] = "M_Jlxy_1_1Unit.L_GoTemp";
        tags[1] = "M_Jlxy_1_1Unit.L_BackTemp";
        tags[2] = "M_Jlxy_1_1Unit.L_Flow";

        HdbClient hdbClient = new HdbClient();
        hdbClient.setLoginInfo("rnzk", "");
        hdbClient.setConfig("10.2.1.53", 9061, 10000);
        HdbData[] hdbData = hdbClient.ReadCurrentByName(tags, false, false, false);
        if (null == hdbData) {
            String errorString = hdbClient.GetErrorString(hdbClient.getLastError());
            System.out.println(errorString);
        } else {
            System.out.println(JSON.toJSONString(hdbData));
        }

    }

    public void connect() {
        try {
            log.info("HdbProperty: {} {} {} {}", HdbProperty.getUser(), HdbProperty.getIp(), HdbProperty.getPort(), HdbProperty.getTimeout());
            userConfig(HdbProperty.getUser());
            serverConfig(HdbProperty.getIp(), HdbProperty.getPort(), HdbProperty.getTimeout());
            log.info("HDB连接{}", hdbClient.isOpen() ? "成功" : "失败");
        } catch (Exception e) {
            log.error("HDB连接: {}", e);
        }

    }


    /**
     * 初始化权限用户配置
     *
     * @param user
     */
    public void userConfig(String user) {
        hdbClient.setLoginInfo(user, "");
    }

    /**
     * 设置连接参数
     *
     * @param serverIp：服务ip
     * @param serverPort：服务端口
     * @param timeout:        服务响应超时时间
     */
    public void serverConfig(String serverIp, int serverPort, int timeout) {
        hdbClient.setConfig(serverIp, serverPort, timeout);
    }


    /**
     * 如：
     * String[] tags = new String[3];
     * tags[0] = "HDB.DEMO计算采集器.R4001";
     * tags[1] = "DEMO计算采集器.R4002";
     * tags[2] = "DEMO计算采集器.B0001";
     *
     * @param tagFullNames
     * @return
     */
    public HdbResponse readCurrentByName(String[] tagFullNames) {
        log.info(JSON.toJSONString("readCurrentByName:" + JSON.toJSONString(tagFullNames)));
        return readCurrentByName(tagFullNames, false, false, false);
    }

    /**
     * 查询多点实时当前数据
     *
     * @param tagFullNames：查询多点全名称
     * @param checkTimeout：是否检测采集超时，超时数据质量返回差
     * @param checkAlarm：是否检测报警，TRUE则HdbData.alarmType保存报警类型
     * @param checkExceeded：是否检测超量程，超量程数据质量返回差
     */
    public HdbResponse readCurrentByName(String[] tagFullNames, boolean checkTimeout, boolean checkAlarm, boolean checkExceeded) {
        HdbData[] hdbData;
        synchronized (hdbClient) {
            hdbData = hdbClient.ReadCurrentByName(tagFullNames, checkTimeout, checkAlarm, checkExceeded);
            log.info("readCurrentByName:" + JSON.toJSONString(hdbData));
            if (null == hdbData) {
                String errorString = hdbClient.GetErrorString(hdbClient.getLastError());
                return HdbResponse.error(errorString);
            }
        }
        return HdbResponse.ok().hdbData(new ArrayList<>(Arrays.asList(hdbData)));
    }


    public boolean isConnected() {
        return this.hdbClient.isOpen();
    }

    public static synchronized GatewayHdbClient getInstance() {
        if (null != instance) {
            return instance;
        }
        instance = new GatewayHdbClient();
        instance.connect();
        return instance;
    }

    public void connectDaemon() {
        Timer timer = new Timer(true);
        final GatewayHdbClient gatewayHdbClient = this;
        TimerTask timerTask = new TimerTask() {
            @Override
            public void run() {
                if (!gatewayHdbClient.isConnected()) {
                    gatewayHdbClient.close();
                    gatewayHdbClient.connect();
                }
            }
        };
        timer.schedule(timerTask, 1000, 1000 * 60);
    }

    public void close() {
        try {
            hdbClient.close();
        } catch (Exception e) {
            log.error("关闭HDB连接: {}", e.getMessage());
        }
    }


    /**
     * 查询多点历史数据，所有点共用一个查询条件cr
     *
     * @param tagFullNames: 查询多点全名称
     * @param cr:           查询条件
     * @param timeout:      查询超时时间,单位：毫秒
     */
    public HdbResponse BatchQueryHistory(String[] tagFullNames, HistoryCriteria cr, int timeout) {
        List<HdbData[]> hdbData = hdbClient.BatchQueryHistory(tagFullNames, cr, timeout);
        if (null == hdbData) {
            String errorString = hdbClient.GetErrorString(hdbClient.getLastError());
            return HdbResponse.error(errorString);
        }
        return HdbResponse.ok();
    }


    /**
     * 控制指令（写点值）
     * FLTHWriteResult为返回结果，具体见控制指令返回查询说明
     *
     * @param user:     写指令的用户（用户要有写实时库权限）,测试可以输system
     * @param fullName: 写的点全名
     * @param value:    写值,必须在点有效量程范围内的值
     * @param reserved: 保留
     * @return
     */
    public HdbResponse WriteTagValue(String user, String fullName, String value, int reserved) {
        FLTHWriteResult flthWriteResult = hdbClient.WriteTagValue(user, fullName, value, reserved);
        if (null == flthWriteResult) {
            String errorString = hdbClient.GetErrorString(hdbClient.getLastError());
            return HdbResponse.error(errorString);
        }
        WriteStatus writeStatus = hdbClient.parseWriteStatus(flthWriteResult);
        switch (writeStatus) {
            case SUCCEED:
                // 表示完成写操作，一般这种情况是对实时库点写
            case EXEC:
                // 表示实时库将写控制信息转发到设备上，实时库正等待最终设备控制结果。
                // 需要循环查询
                flthWriteResult = queryCurrentWriteResult(flthWriteResult);
            case FAILED:
            case NONE:
        }
        return HdbResponse.ok();
    }

    public HdbResponse WriteTagValue(String user, String fullName, String value) {
        return WriteTagValue(user, fullName, value, 0);
    }

    /**
     * 批量控制指令，每点有一个写值
     *
     * @param user:         写指令的用户（用户要有写实时库权限）
     * @param tagFullNames: 写多点全名称
     * @param values:       写多值（每点）
     * @param reserved:     保留
     * @return
     */
    public HdbResponse WriteBatchTagValues(String user, String[] tagFullNames, String[] values, int reserved) {
        HdbResponse response;
        synchronized (hdbClient) {
            response = HdbResponse.ok();
            List<FLTHWriteResult> flthWriteResults = hdbClient.WriteBatchTagValues(user, tagFullNames, values, reserved);
            response.setExecResults(flthWriteResults);
            log.info("WriteBatchTagValues  flthWriteResults: " + JSON.toJSONString(flthWriteResults));
        }
//        int n = 20;
//        while (CollectionUtil.isNotEmpty(response.getExecResults())) {
//            parseWriteStatus(response);
//            try {
//                TimeUnit.SECONDS.sleep(3);
//            } catch (InterruptedException e) {
//                log.error("", e);
//            }
//            n--;
//        }
        return response;
    }

    public HdbResponse  WriteBatchTagValues(String user, String[] tagFullNames, String[] values) {
        return WriteBatchTagValues(user, tagFullNames, values, 0);
    }

    public HdbResponse WriteBatchTagValues(String[] tagFullNames, String[] values) {
        return WriteBatchTagValues(HdbProperty.getUser(), tagFullNames, values);
    }


    public void parseWriteStatus(HdbResponse response) {
        List<FLTHWriteResult> results = response.getExecResults();

        List<HdbWriteResult> writeResults = new ArrayList<>();
        List<FLTHWriteResult> execResults = new ArrayList<>();
        for (FLTHWriteResult result : results) {
            HdbWriteResult hdbWriteResult = new HdbWriteResult();
            WriteStatus writeStatus = hdbClient.parseWriteStatus(result);
            switch (writeStatus) {
                case SUCCEED:
                    // 表示完成写操作，一般这种情况是对实时库点写
                    break;
                case EXEC:
                    // 表示实时库将写控制信息转发到设备上，实时库正等待最终设备控制结果。
                    // 需要循环查询
//                    result = queryCurrentWriteResult(result);
                    execResults.add(result);
                    break;
                case FAILED:
                    // 表示实时库对这个写控制失败,可以进一步调用GetErrorString函数获取错误具体原因。
                case NONE:
                    // 表示实时库对这个写控制失败,可以进一步调用GetErrorString函数获取错误具体原因。
                    String errorString = hdbClient.GetErrorString(result.ReplyError);
                    hdbWriteResult.setErrorString(errorString);
            }
            hdbWriteResult.setFlthWriteResult(result);
            hdbWriteResult.setWriteStatus(writeStatus);
            writeResults.add(hdbWriteResult);
        }
        response.addWriteResults(writeResults);
        response.setExecResults(execResults);
    }

    /**
     * 查询控制指令（写点值）执行状态
     *
     * @param flthWriteResult: 查询的指令编号（FLTHWriteResult.Version）
     * @return
     */
    public FLTHWriteResult queryCurrentWriteResult(FLTHWriteResult flthWriteResult) {
        long[] versions = new long[1];
        versions[0] = flthWriteResult.getVersion();
        List<FLTHWriteResult> flthWriteResults = hdbClient.QueryCurrentWriteResult(versions);
        if (CollectionUtil.isNotEmpty(flthWriteResults)) {
            return flthWriteResults.get(0);
        }
        return null;
    }


    public List<FLTHWriteResult> queryCurrentWriteResult(List<FLTHWriteResult> flthWriteResults) {
        long[] versions = new long[flthWriteResults.size()];
        for (int i = 0; i < flthWriteResults.size(); i++) {
            versions[i] = flthWriteResults.get(i).getVersion();
        }
        return hdbClient.QueryCurrentWriteResult(versions);
    }


    /**
     * 向实时库上传数据, 数据的时间戳以采集器或底层设备时钟为准
     *
     * @param tagFullNames: 多点全名称
     * @param tagDatas:     多点数据(数组长度应与tagFullNames数组长度相等)
     * @return
     */
    public HdbResponse forwardDataByName(String[] tagFullNames, HdbClctrData[] tagDatas) {
        FLTHClctrResult flthClctrResult = hdbClient.ForwardDataByName(tagFullNames, tagDatas);
        if (null == flthClctrResult) {
            String errorString = hdbClient.GetErrorString(hdbClient.getLastError());
            return HdbResponse.error(errorString);
        }
        int error = flthClctrResult.getError(); // 0表示调用成功，（但不代表每个点上传数据成功）
        if (error != 0) {
            // 所有点位上报不成功
            return HdbResponse.error(hdbClient.GetErrorString(error));
        }
        List<Integer> aDataList = flthClctrResult.getAData();
        for (int i = 0; i < aDataList.size(); i++) {
            Integer aData = aDataList.get(i);
            if (aData != 0) {
                String errorString = hdbClient.GetErrorString(aData);
                return HdbResponse.error(String.format("点%s: %s", tagFullNames[i], errorString));
            }
        }
        return HdbResponse.ok();
    }


}
